home *** CD-ROM | disk | FTP | other *** search
/ Aminet 35 / Aminet 35 (2000)(Schatztruhe)[!][Feb 2000].iso / Aminet / gfx / misc / gnuplot-src.lha / gnuplot-3.7.1src / gnuplot-3.7.1.lha / gnuplot-3.7.1 / amiga.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-15  |  10.9 KB  |  374 lines

  1. /* $Id: amigRÍ*98/04/15 19:21:59 lhecking Exp $ */
  2.  
  3. /* GNUPLOT - amiga.c */
  4.  
  5. /*[
  6.  * Copyright 1986 - 1993, 1998   Thomas Williams, Colin Kelley
  7.  *
  8.  * Permission to use, copy, and distribute this software and its
  9.  * documentation for any purpose with or without fee is hereby granted,
  10.  * provided that the above copyright notice appear in all copies and
  11.  * that both that copyright notice and this permission notice appear
  12.  * in supporting documentation.
  13.  *
  14.  * Permission to modify the software is granted, but not the right to
  15.  * distribute the complete modified source code.  Modifications are to
  16.  * be distributed as patches to the released version.  Permission to
  17.  * distribute binaries produced by compiling modified sources is granted,
  18.  * provided you
  19.  *   1. distribute the corresponding source modifications from the
  20.  *    released version in the form of a patch file along with the binaries,
  21.  *   2. add special version identification to distinguish your version
  22.  *    in addition to the base release version number,
  23.  *   3. provide your name and address as the primary contact for the
  24.  *    support of your modified version, and
  25.  *   4. retain our contact information in regard to use of the base
  26.  *    software.
  27.  * Permission to distribute the released version of the source code along
  28.  * with corresponding source modifications in the form of a patch file is
  29.  * granted with same provisions 2 through 4 for binary distributions.
  30.  *
  31.  * This software is provided "as is" without express or implied warranty
  32.  * to the extent permitted by applicable law.
  33. ]*/
  34.  
  35. /*
  36.  * amiga.c
  37.  *
  38.  * Written by Carsten Steger <stegerc@informatik.tu-muenchen.de>
  39.  *
  40.  * Popen and pclose have the same semantics as their UNIX counterparts.
  41.  *
  42.  * Additionally, they install an exit trap that closes all open pipes,
  43.  * should the program terminate abnormally.
  44.  */
  45.  
  46.  
  47. #include <stdio.h>
  48. #include <ios1.h>
  49. #include <error.h>
  50. #include <string.h>
  51. #include <stdlib.h>
  52.  
  53. #include <exec/types.h>
  54. #include <dos/dos.h>
  55. #include <dos/dosextens.h>
  56. #include <dos/dostags.h>
  57. #include <proto/exec.h>
  58. #include <proto/dos.h>
  59.  
  60. #ifdef PIPES  /* dont bother if pipes are not being used elsewhere */
  61.  
  62. /* Maximum number of open pipes. If this is set to a number > 10, the code
  63.  * that constructs the pipe names in popen () will have to be modified.
  64.  */
  65. #define MAX_OPEN_PIPES 10
  66.  
  67. /* We need at least this Dos version to work. */
  68. #define DOS_VERSION 37
  69.  
  70.  
  71. /* This data structure is sent to the child process with sm_Cmd set to the
  72.  * command to be executed. When the child is done it sets sm_RetCode to
  73.  * the return code of the executed command.
  74.  */
  75. struct StartupMessage {
  76.   struct Message sm_Msg;
  77.   LONG sm_RetCode;
  78.   UBYTE *sm_Cmd;
  79. };
  80.  
  81. /* We keep track of the open pipe through this data structure. */
  82. struct PipeFileDescriptor {
  83.   FILE *pfd_File;
  84.   struct StartupMessage pfd_Msg;
  85. };
  86.  
  87.  
  88. /* Needed to check for the required Dos version. */
  89. extern struct DosLibrary *DOSBase;
  90.  
  91. /* This data structure keeps track of the pipes that are still open. */
  92. static struct PipeFileDescriptor OpenPipes[MAX_OPEN_PIPES];
  93.  
  94. /* The address of the process that calls popen or pclose. */
  95. static struct Process *ThisProcess;
  96.  
  97. /* Are we called for the first time? */
  98. static LONG FirstCall = TRUE;
  99.  
  100.  
  101. /* Prototypes for the functions below. */
  102. FILE *popen (const char *command, const char *mode);
  103. int pclose (FILE *stream);
  104. static void CleanUpPipes (void);
  105. static int __saveds ChildEntry (void);
  106.  
  107.  
  108. FILE *popen (command, mode)
  109. const char *command;
  110. const char *mode;
  111. {
  112.   UBYTE PipeName[16];
  113.   ULONG ProcAddress;
  114.   UBYTE HexDigit;
  115.   UBYTE *NextChar;
  116.   struct CommandLineInterface *ThisCli;
  117.   struct PipeFileDescriptor *PipeToUse;
  118.   LONG PipeNumToUse;
  119.   LONG ChildPipeMode;
  120.   BPTR ChildPipe;
  121.   FILE *ParentPipe;
  122.   struct Process *ChildProcess;
  123.   struct TagItem NewProcTags[8] = {
  124.     {NP_Entry, (Tag) ChildEntry},
  125.     {NP_Cli, TRUE},
  126.     {NP_StackSize, 4096},
  127.     {NP_Input, NULL},
  128.     {NP_Output, NULL},
  129.     {NP_CloseInput, FALSE},
  130.     {NP_CloseOutput, FALSE},
  131.     {TAG_DONE, 0}
  132.   };
  133.  
  134.   /* Test whether we're using the right Dos version. */
  135.   if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
  136.     errno = EPIPE;
  137.     return NULL;
  138.   }
  139.  
  140.   /* If we're called for the first time, install exit trap and do some
  141.    * initialisation stuff.
  142.    */
  143.   if (FirstCall) {
  144.     /* Initialise pipe file descriptor table. */
  145.     memset (OpenPipes, 0, sizeof (OpenPipes));
  146.     
  147.     /* Install our exit trap. */
  148.     if (atexit (CleanUpPipes) != 0) {
  149.       errno = EPIPE;
  150.       return NULL;
  151.     }
  152.     FirstCall = FALSE;
  153.   }
  154.  
  155.   /* If we don't know our process' address yet, we should get it now. */
  156.   if (ThisProcess == NULL)
  157.     ThisProcess = (struct Process *) FindTask (NULL);
  158.  
  159.   /* Get our Cli structure. */
  160.   ThisCli = Cli ();
  161.  
  162.   /* Now try to find an empty slot in the pipe file descriptor table.
  163.    * Return NULL if no slot is available.
  164.    */
  165.   for (PipeNumToUse = 0; PipeNumToUse < MAX_OPEN_PIPES; PipeNumToUse++)
  166.     if (OpenPipes[PipeNumToUse].pfd_File == NULL) break;
  167.   if (PipeNumToUse >= MAX_OPEN_PIPES) {
  168.     errno = EMFILE;
  169.     return NULL;
  170.   }
  171.   PipeToUse = &OpenPipes[PipeNumToUse];
  172.  
  173.   /* Check if the specified mode is valid. */
  174.   if (strcmp (mode, "r") == 0)
  175.     ChildPipeMode = MODE_NEWFILE;
  176.   else if (strcmp (mode, "w") == 0)
  177.     ChildPipeMode = MODE_OLDFILE;
  178.   else {
  179.     errno = EINVAL;
  180.     return NULL;
  181.   }
  182.  
  183.   /* Make a unique file name for the pipe that we are about to open. The
  184.    * file name has the following format: "PIPE:XXXXXXXX_Y", where
  185.    * XXXXXXXX is the address of our process in hex, Y is the number of the
  186.    * slot in the pipe descriptor table that we will use. The code is
  187.    * equivalent to
  188.    * sprintf (PipeNameWriter, "PIPE:%08lX_%1d", ThisProcess, PipeNumToUse);
  189.    * but it doesn't need sprintf and therefore makes programs that don't
  190.    * use printf a lot shorter.
  191.    */
  192.   strcpy (PipeName, "PIPE:00000000_0");
  193.   NextChar = PipeName + 12;
  194.   ProcAddress = (ULONG) ThisProcess;
  195.   while (ProcAddress != 0) {
  196.     HexDigit = (UBYTE) ProcAddress & 0xf;
  197.     HexDigit = HexDigit < 10 ? HexDigit + '0' : HexDigit - 10 + 'A';
  198.     *NextChar-- = HexDigit;
  199.     ProcAddress >>= 4;
  200.   }
  201.   /* If MAX_OPEN_PIPES > 10, this will have to be modified. */
  202.   PipeName[14] = ((UBYTE) PipeNumToUse) + '0';
  203.  
  204.   /* Create tags for the child process. */
  205.   if (ThisProcess->pr_CLI)
  206.     NewProcTags[2].ti_Data = ThisCli->cli_DefaultStack << 2;
  207.   else
  208.     NewProcTags[2].ti_Data = ThisProcess->pr_StackSize;
  209.  
  210.   /* Open both ends of the pipe. The child's side is opened with Open (),
  211.    * while the parent's side is opened with fopen ().
  212.    */
  213.   ChildPipe = Open (PipeName, ChildPipeMode);
  214.   ParentPipe = fopen (PipeName, mode);
  215.   if (ChildPipeMode == MODE_NEWFILE) {
  216.     NewProcTags[3].ti_Data = Input ();
  217.     NewProcTags[4].ti_Data = ChildPipe;
  218.     NewProcTags[5].ti_Data = FALSE;
  219.     NewProcTags[6].ti_Data = TRUE;
  220.   } else {
  221.     NewProcTags[3].ti_Data = ChildPipe;
  222.     NewProcTags[4].ti_Data = Output ();
  223.     NewProcTags[5].ti_Data = TRUE;
  224.     NewProcTags[6].ti_Data = FALSE;
  225.   }
  226.   if (ChildPipe == NULL || ParentPipe == NULL) {
  227.     errno = EPIPE;
  228.     goto cleanup;
  229.   }
  230.  
  231.   /* Now generate a entry in the pipe file descriptor table. */
  232.   PipeToUse->pfd_Msg.sm_Cmd = malloc (strlen (command) + 1);
  233.   if (PipeToUse->pfd_Msg.sm_Cmd == NULL) {
  234.     errno = ENOMEM;
  235.     goto cleanup;
  236.   }
  237.   strcpy (PipeToUse->pfd_Msg.sm_Cmd, command);
  238.   PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort = CreateMsgPort ();
  239.   if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL) {
  240.     errno = ENOMEM;
  241.     goto cleanup;
  242.   }
  243.   PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
  244.   PipeToUse->pfd_Msg.sm_Msg.mn_Node.ln_Pri = 0;
  245.   PipeToUse->pfd_Msg.sm_Msg.mn_Length = sizeof (struct StartupMessage);
  246.   PipeToUse->pfd_File = ParentPipe;
  247.  
  248.   /* Now create the child process. */
  249.   ChildProcess = CreateNewProc (NewProcTags);
  250.   if (ChildProcess == NULL) {
  251.     errno = ENOMEM;
  252.     goto cleanup;
  253.   }
  254.  
  255.   /* Pass the startup message to the child process. */
  256.   PutMsg (&ChildProcess->pr_MsgPort, (struct Message *) &PipeToUse->pfd_Msg);
  257.  
  258.   /* This is the normal exit point for the function. */
  259.   return ParentPipe;
  260.  
  261.   /* This code is only executed if there was an error. In this case the
  262.    * allocated resources must be freed. The code is actually clearer (at
  263.    * least in my opinion) and more concise by using goto than by using a
  264.    * function (global variables or function parameters needed) or a lot
  265.    * of if-constructions (code gets blown up unnecessarily).
  266.    */
  267. cleanup:
  268.   if (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort == NULL)
  269.     DeleteMsgPort (PipeToUse->pfd_Msg.sm_Msg.mn_ReplyPort);
  270.   if (ParentPipe)
  271.     fclose (ParentPipe);
  272.   if (ChildPipe)
  273.     Close (ChildPipe);
  274.   return NULL;
  275. }
  276.  
  277.  
  278. int pclose (stream)
  279. FILE *stream;
  280. {
  281.   LONG PipeToClose;
  282.  
  283.   /* Test whether we're using the right Dos version. */
  284.   if (DOSBase->dl_lib.lib_Version < DOS_VERSION) {
  285.     errno = EPIPE;
  286.     return -1;
  287.   }
  288.  
  289.   /* Test whether this is the first call to this module or not. If so,
  290.    * pclose has been called before popen and we return with an error
  291.    * because the initialisation has yet to be done.
  292.    */
  293.   if (FirstCall) {
  294.     errno = EBADF;
  295.     return -1;
  296.   }
  297.  
  298.   /* Search for the correct table entry and close the associated file. */
  299.   for (PipeToClose = 0; PipeToClose < MAX_OPEN_PIPES; PipeToClose++)
  300.     if (OpenPipes[PipeToClose].pfd_File == stream) break;
  301.   if (PipeToClose >= MAX_OPEN_PIPES) {
  302.     errno = EBADF;
  303.     return -1;
  304.   }
  305.   fclose (stream);
  306.  
  307.   /* Now wait for the child to terminate and get its exit status. */
  308.   WaitPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
  309.   OpenPipes[PipeToClose].pfd_File = NULL;
  310.  
  311.   /* Free the allocates resources. */
  312.   DeleteMsgPort (OpenPipes[PipeToClose].pfd_Msg.sm_Msg.mn_ReplyPort);
  313.   free (OpenPipes[PipeToClose].pfd_Msg.sm_Cmd);
  314.  
  315.   return OpenPipes[PipeToClose].pfd_Msg.sm_RetCode;
  316. }
  317.  
  318.  
  319. static void CleanUpPipes ()
  320. {
  321.   LONG Count;
  322.   FILE *Pipe;
  323.  
  324.   /* Close each open pipe. */
  325.   for (Count = 0; Count < MAX_OPEN_PIPES; Count++) {
  326.     Pipe = OpenPipes[Count].pfd_File;
  327.     if (Pipe != NULL)
  328.       pclose (Pipe);
  329.   }
  330. }
  331.  
  332.  
  333. static int __saveds ChildEntry ()
  334. {
  335.   struct Process *ChildProc;
  336.   struct StartupMessage *StartUpMessage;
  337.   LONG ReturnCode;
  338.   struct DosLibrary *DOSBase;
  339.   struct TagItem SysTags[3] = {
  340.     {SYS_Asynch, FALSE},
  341.     {SYS_UserShell, TRUE},
  342.     {TAG_DONE, 0}
  343.   };
  344.  
  345.   /* We need to open this library, because we don't inherit it from our
  346.    * parent process.
  347.    */
  348.   DOSBase = (struct DosLibrary *) OpenLibrary ("dos.library", DOS_VERSION);
  349.  
  350.   /* Get the childs process structure. */
  351.   ChildProc = (struct Process *) FindTask (NULL);
  352.  
  353.   /* Wait for the startup message from the parent. */
  354.   WaitPort (&ChildProc->pr_MsgPort);
  355.   StartUpMessage = (struct StartupMessage *) GetMsg (&ChildProc->pr_MsgPort);
  356.  
  357.   /* Now run the command and return the result. */
  358.   if (DOSBase != NULL)
  359.     ReturnCode = System (StartUpMessage->sm_Cmd, SysTags);
  360.   else
  361.     ReturnCode = 10000;
  362.   StartUpMessage->sm_RetCode = ReturnCode;
  363.  
  364.   /* Tell the parent that we are done. */
  365.   ReplyMsg ((struct Message *) StartUpMessage);
  366.  
  367.   if (DOSBase)
  368.     CloseLibrary ((struct Library *) DOSBase);
  369.  
  370.   return 0;
  371. }
  372.  
  373. #endif /* PIPES */
  374.